iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
自我挑戰組

從 Python 開發者的角度學習 Rust —— 從語法基礎到實戰應用系列 第 27 篇

[Day 27] Rust 的 Web 應用(四):Rust + React 全端開發

  • 分享至 

  • xImage
  •  

在前一篇文章當中我們展示了如何使用 Rust 當中的 Rocket 以及 mongoDB 套件,建立了一個簡單的使用者資料管理方面的 RESTful API,但是總不能叫使用者只能透過 API 直接與資料庫溝通吧,這樣的話就不叫作 Web 應用了,當然還需要有前端作為使用者的操作介面囉,這時候我們就可以與之前提到的前端框架 React 進行結合,並且只需要簡單的幾個步驟即可完成這樣的全端開發。

一、Rust + React 專案的檔案結構

1. 建立 React App

為了要沿用上一篇的專案內容的 RESTful API 作為資料庫互動的後端,我們現在需要在既有的檔案結構下新增 React 專案,假設上一篇的檔案結構最後長這樣, rust-restful-api 專案是被放在 myproject 內:

myproject/
└── rust-restful-api/
    ├── src/
    │   └── main.rs
    ├── target/
    ├── .gitignore
    ├── Cargo.lock
    └── Cargo.toml

那我們現在可以在 myproject 路徑下在終端機輸入指令

npx create-react-app frontend

這樣就會建立一個名為 frontend 的前端專用資料夾,接著我們直接進入該資料夾,執行打包的指令

cd  frontend
npm run build

完成之後檔案結構會長這樣

myproject/
├── frontend/
│   ├── build/
│   ├── node_modules/
│   ├── public/
│   ├── src/
│   ├── .gitignore
│   ├── package-lock.json
│   ├── package.json
│   └── README.md
└── rust-restful-api/
    ├── src/
    └── target/
    └──.gitignore
    └──Cargo.lock
    └──Cargo.toml

接下來,我們要先處理 Rust 這邊讀取前端網頁的程式碼

2. 首頁指向 React 網頁

首先,修改一下 Rust 的 main.rs,我們直接看修改後的完整程式碼,基於上一篇的範例修改的

use futures::stream::TryStreamExt; // 導入 TryStreamExt,提供 `try_collect` 方法以簡化處理 MongoDB 查詢結果
use mongodb::{bson::doc, bson::oid::ObjectId, options::ClientOptions, Client}; // 導入 MongoDB 客戶端、選項及 BSON 文檔的處理工具
use rocket::{delete, fs::FileServer, get, post, put, routes, serde::json::Json, State}; // 導入 Rocket 框架的路由、HTTP 請求方法及狀態管理
use serde::{Deserialize, Serialize}; // 導入 Serde 序列化及反序列化,讓結構體能夠轉換成 JSON

// 定義一個用戶結構,支援 JSON 的序列化和反序列化,並實現 Debug 和 Clone 特徵
#[derive(Debug, Deserialize, Serialize, Clone)]
struct User {
    #[serde(skip_serializing_if = "Option::is_none")] // 如果 id 欄位是 None,序列化時會跳過該欄位
    id: Option<String>, // 用戶的 ID,可能不存在(可選)
    name: String, // 用戶的名字,為必填欄位
}

// Rocket 主程式入口,使用 async 函數以支援異步操作
#[rocket::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 設置 MongoDB 連接選項,使用 MongoDB Atlas 提供的 URL 進行連接
    let client_options = ClientOptions::parse("mongodb+srv://masoufo0310:2ioxaagu@database.541vj.mongodb.net/?retryWrites=true&w=majority&appName=Database").await?;
    let client = Client::with_options(client_options)?; // 建立 MongoDB 客戶端連接

    let _db = client.database("testdb"); // 取得指定的資料庫 testdb

    // 構建 Rocket 應用,並管理 MongoDB 客戶端狀態
    rocket::build()
        .manage(client) // 傳遞 MongoDB 客戶端到 Rocket 管理狀態
        .mount(
            // 註冊路由,將所有 CRUD 操作綁定到應用上
            "/",
            routes![create_user, get_users, update_user, delete_user], // 綁定路由,處理用戶的新增、查詢、更新、刪除
        )
        .mount("/", FileServer::from("../frontend/build")) // 讓 Rocket 提供 React build 目錄中的靜態資源
        .ignite() // 啟動 Rocket 應用,初始化伺服器
        .await?
        .launch() // 啟動伺服器,並開始監聽請求
        .await?;

    Ok(()) // 正常退出時返回 OK
}

// POST 請求,用來新增用戶
#[post("/users", data = "<user>")]
async fn create_user(client: &State<Client>, user: Json<User>) -> Json<User> {
    let collection = client.database("testdb").collection::<User>("users"); // 連接到 MongoDB 的 testdb 資料庫,並選擇 users 集合
    let new_user = User {
        id: None,                // 新增用戶時,ID 設為 None,由 MongoDB 自動生成
        name: user.name.clone(), // 複製請求中的用戶名稱
    };

    collection.insert_one(new_user.clone(), None).await.unwrap(); // 將新用戶資料插入資料庫,忽略錯誤處理
    Json(new_user) // 回傳新增的用戶資料作為 JSON
}

// GET 請求,用來獲取所有用戶資料
#[get("/users")]
async fn get_users(client: &State<Client>) -> Json<Vec<User>> {
    let collection = client.database("testdb").collection::<User>("users"); // 連接到 MongoDB 的 testdb 資料庫,並選擇 users 集合

    let cursor = collection.find(None, None).await.unwrap(); // 從集合中查詢所有用戶,獲取 MongoDB 游標
    let users: Vec<User> = cursor.try_collect().await.unwrap(); // 將游標中的用戶資料轉換為 Vec<User>

    Json(users) // 回傳所有用戶的 JSON 數據
}

// PUT 請求,用來更新指定 ID 的用戶資料
#[put("/users/<id>", data = "<user>")]
async fn update_user(client: &State<Client>, id: String, user: Json<User>) -> Json<User> {
    let collection = client.database("testdb").collection::<User>("users");

    // 將 id 轉換為 ObjectId
    let object_id = match ObjectId::parse_str(&id) {
        Ok(oid) => oid,
        Err(_) => {
            return Json(User {
                id: Some(id),
                name: "Invalid ID format".to_string(),
            })
        }
    };

    let filter = doc! { "_id": object_id }; // 根據 ObjectId 進行過濾
    let update = doc! { "$set": { "name": &user.name } }; // 設置更新條件

    collection.update_one(filter, update, None).await.unwrap();
    Json(User {
        id: Some(id),            // 更新後的用戶 ID 設為指定的 ID
        name: user.name.clone(), // 返回更新後的用戶名字
    })
}

// DELETE 請求,用來刪除指定 ID 的用戶
#[delete("/users/<id>")]
async fn delete_user(client: &State<Client>, id: String) -> String {
    let collection = client.database("testdb").collection::<User>("users"); // 連接到 MongoDB 的 testdb 資料庫,並選擇 users 集合

    // 將傳入的 id 轉換為 ObjectId,如果失敗則回傳錯誤訊息
    let object_id = match ObjectId::parse_str(&id) {
        Ok(oid) => oid,
        Err(_) => return format!("Invalid ID format: {}", id),
    };

    let filter = doc! { "_id": object_id }; // 使用 BSON 格式,根據 ObjectId 進行過濾

    // 執行刪除操作,忽略錯誤處理
    match collection.delete_one(filter, None).await {
        Ok(result) => {
            if result.deleted_count == 1 {
                format!("User with id {} deleted", id) // 回傳刪除成功訊息
            } else {
                format!("No user found with id {}", id) // 如果沒有找到用戶,回傳未找到訊息
            }
        }
        Err(_) => format!("Failed to delete user with id {}", id), // 刪除失敗時回傳錯誤訊息
    }
}

這段程式碼 98% 都跟上一篇的一模一樣,只有少少的新增了兩行:

  1. use rocket::{delete, fs::FileServer, get, post, put, routes, serde::json::Json, State}; ,在這當中加入了 fs::FileServer,它的作用是讓 Rocket 提供靜態資源。這意味著,我們可以指向一個特定的目錄,Rocket 會將該目錄中的檔案(如 HTML、JavaScript、CSS 等)直接提供給用戶端。在這個案例中,我們將 React 前端應用打包後的靜態檔案放在 ../frontend/build 目錄中,而 FileServer 就是負責將這些靜態檔案提供給用戶。
  2. .mount("/", FileServer::from("../frontend/build")) ,在 rocket::build 內加入了當使用者進入 / 路徑時,以 ../frontend/build 作為 FileServer 他就是預設將該路徑資料夾中的 index.html 作為回傳的檔案內容,並且也能讓用戶讀取得到裡面的 static 靜態資源。

實際執行的時間到了

cargo run

執行後可以看到以下畫面出現在 http://127.0.0.1:8000

https://ithelp.ithome.com.tw/upload/images/20241010/20121176raAQVS0YkN.png

就代表我們已經透過 rocket 完整的結合了 React 跟 Rust 的前後端,接下來讓我們完成前端用戶介面吧

3. 建立用戶資料管理介面

現在我們可以再開一個終端機,以區別原本的後端所使用的終端機,並且先進入 React資料夾下

cd frontend

我們會需要透過前端去呼叫 API ,所以這邊先用指令安裝一下 React 常用的 request 套件 axios

npm install axios

完成套件安裝之後,我們需要修改兩個檔案,首先第一個檔案是位於 src 之下的 App.js,他原本的內容應該是長這樣的

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

可能有人對於 React 還並不是很認識,但我們就先略過關於這段程式碼當中的介紹,先以概念與流程為主,簡單來說,這個 App.js 的內容就是我們剛剛上面網頁中所呈現的內容主體,因此我們只需要修改這個 App.js 就可以重新建立我們的首頁畫面,接下來我們就直接進行修改,修改後的 App.js 內容如下:

import { useState, useEffect } from 'react'; // 從 React 中導入 useState 和 useEffect,用來管理狀態與副作用
import axios from 'axios'; // 從 axios 套件導入,用來處理 HTTP 請求
import './App.css'; // 導入外部的 CSS 檔案,設置樣式

function App() {
  const [users, setUsers] = useState([]); // 定義一個狀態變數 users,用來儲存用戶列表,初始值為空陣列
  const [newUserName, setNewUserName] = useState(''); // 定義一個狀態變數 newUserName,用來儲存新增用戶的名字,初始值為空字串
  const [updateUserName, setUpdateUserName] = useState(''); // 定義一個狀態變數 updateUserName,用來儲存更新用戶的名字,初始值為空字串
  const [userIdToUpdate, setUserIdToUpdate] = useState(''); // 定義一個狀態變數 userIdToUpdate,用來儲存需要更新的用戶 ID,初始值為空字串

  // 在組件首次渲染後,執行獲取用戶列表的函數
  useEffect(() => {
    fetchUsers(); // 執行獲取用戶的函數
  }, []); // 這裡的空陣列表示只在組件首次載入時執行一次

  // 獲取用戶列表的非同步函數
  const fetchUsers = async () => {
    try {
      const response = await axios.get('/users'); // 向後端發送 GET 請求,獲取用戶資料
      setUsers(response.data); // 將獲取到的用戶資料設定到 users 狀態中
    } catch (error) {
      console.error('Error fetching users:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  // 新增用戶的非同步函數
  const createUser = async () => {
    try {
      if (!newUserName) return; // 如果沒有輸入用戶名稱,則不執行新增操作
      await axios.post('/users', { name: newUserName }); // 向後端發送 POST 請求,傳送新用戶資料
      setNewUserName(''); // 清空輸入框的內容
      fetchUsers(); // 重新獲取用戶列表,更新畫面
    } catch (error) {
      console.error('Error creating user:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  // 刪除用戶的非同步函數
  const deleteUser = async (id) => {
    try {
      await axios.delete(`/users/${id}`); // 向後端發送 DELETE 請求,刪除指定 ID 的用戶
      fetchUsers(); // 重新獲取用戶列表,更新畫面
    } catch (error) {
      console.error('Error deleting user:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  // 更新用戶的非同步函數
  const updateUser = async () => {
    try {
      if (!userIdToUpdate || !updateUserName) return; // 如果沒有輸入要更新的用戶 ID 或新名稱,則不執行更新操作
      await axios.put(`/users/${userIdToUpdate}`, { name: updateUserName }); // 向後端發送 PUT 請求,更新指定 ID 的用戶資料
      setUserIdToUpdate(''); // 清空輸入框的內容
      setUpdateUserName(''); // 清空輸入框的內容
      fetchUsers(); // 重新獲取用戶列表,更新畫面
    } catch (error) {
      console.error('Error updating user:', error); // 如果發生錯誤,顯示錯誤訊息
    }
  };

  return (
    <div className="App"> {/* 最外層的 div,套用 CSS 樣式 App */}
      <header className="App-header"> {/* 頁面的 header,套用 CSS 樣式 App-header */}
        <h1>會員資料管理系統</h1> {/* 頁面標題 */}

        <div className="form-section"> {/* 用於表單的區塊,包含新增和更新會員 */}
          <div className="form-group"> {/* 表單組塊,用於新增會員 */}
            <h2>新增會員</h2> {/* 小標題:新增會員 */}
            <input
              type="text"
              value={newUserName} // 綁定新用戶名稱的狀態變數
              onChange={(e) => setNewUserName(e.target.value)} // 當輸入變更時,更新 newUserName 狀態
              placeholder="輸入會員名稱" // 提示使用者輸入會員名稱
            />
            <button onClick={createUser}>新增會員</button> {/* 點擊按鈕時執行 createUser 函數 */}
          </div>

          <div className="form-group"> {/* 表單組塊,用於更新會員 */}
            <h2>更新會員</h2> {/* 小標題:更新會員 */}
            <input
              type="text"
              value={userIdToUpdate} // 綁定需要更新的會員 ID 的狀態變數
              onChange={(e) => setUserIdToUpdate(e.target.value)} // 當輸入變更時,更新 userIdToUpdate 狀態
              placeholder="輸入會員ID" // 提示使用者輸入會員 ID
            />
            <input
              type="text"
              value={updateUserName} // 綁定新的會員名稱的狀態變數
              onChange={(e) => setUpdateUserName(e.target.value)} // 當輸入變更時,更新 updateUserName 狀態
              placeholder="輸入新的會員名稱" // 提示使用者輸入新的會員名稱
            />
            <button onClick={updateUser}>更新會員</button> {/* 點擊按鈕時執行 updateUser 函數 */}
          </div>
        </div>

        <div className="table-section"> {/* 表格區塊,用於顯示會員列表 */}
          <h2>會員列表</h2> {/* 小標題:會員列表 */}
          <table className="user-table"> {/* 使用者表格,套用 CSS 樣式 user-table */}
            <thead>
              <tr>
                <th>會員ID</th> {/* 表格標題:會員 ID */}
                <th>會員名稱</th> {/* 表格標題:會員名稱 */}
                <th>操作</th> {/* 表格標題:操作 */}
              </tr>
            </thead>
            <tbody>
              {users.map((user) => ( // 遍歷 users 陣列,顯示每個用戶的資料
                <tr key={user.id}> {/* 每一列的唯一 key 為 user.id */}
                  <td>{user.id}</td> {/* 顯示用戶的 ID */}
                  <td>{user.name}</td> {/* 顯示用戶的名稱 */}
                  <td>
                    <button className="edit-btn" onClick={() => deleteUser(user.id)}>刪除</button> {/* 刪除按鈕,點擊後執行 deleteUser 函數 */}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </header>
    </div>
  );
}

export default App; // 導出 App 組件,以便在其他地方使用

修改完畢之後,我們要針對網頁的樣式進一步修改,所以我們也同步修改這個 App.css 的內容如下:

.App {
    text-align: center;
    background-color: #f9f3e7;
    color: #4f4f4f;
    font-family: 'Arial', sans-serif;
}

.App-header {
    background-color: #f3e5d5;
    padding: 20px;
    border-radius: 10px;
}

h1 {
    color: #c48d41;
}

.form-section {
    display: flex;
    justify-content: space-around;
    margin: 20px 0;
}

.form-group {
    background-color: #fff4e6;
    padding: 15px;
    border-radius: 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

input {
    margin: 10px;
    padding: 10px;
    border: 2px solid #c48d41;
    border-radius: 5px;
}

button {
    padding: 10px 20px;
    background-color: #e07a5f;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

button:hover {
    background-color: #c5533d;
}

.table-section {
    margin-top: 20px;
}

.user-table {
    width: 80%;
    margin: 0 auto;
    border-collapse: collapse;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.user-table th,
.user-table td {
    padding: 10px 15px;
    border: 1px solid #ddd;
}

.user-table th {
    background-color: #f7d6ad;
    color: #4f4f4f;
}

.user-table td {
    background-color: #fffaf0;
}

.edit-btn {
    background-color: #e07a5f;
    color: white;
    padding: 5px 10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.edit-btn:hover {
    background-color: #c5533d;
}

完成之後,我們再次打包,每次前端修改完畢之後,都要重新打包,這樣才能刷新後端那邊讀取到的 build 資料夾內的檔案

npm run build

打包完畢之後,我們再次查看前端網頁現在的樣子,後端這邊也需要重新載入,只需要按下 Ctrl + F5 刷新即可

https://ithelp.ithome.com.tw/upload/images/20241010/201211769xquy1sL51.png

現在我們就可以透過前端介面跟資料庫溝通囉,如果我們要修改網頁內容,就只需要更改 App.js 或者 App.css 之後,用 npm run build 重新打包就可以了,這樣我們就可以同時使用 React 與 Rust 的高效能進行開發了

上面展示了我們將兩者之間結合的結果,現在我們要開始解析 App.js 的網頁內容了

二、關於 App.js 的詳細說明

在這個段落,我們將深入探討 App.js 檔案中的核心概念,這些概念能夠幫助你更好地理解如何透過 React 與後端 API 進行互動,並創建一個有效的前端資料管理介面。

1. 狀態管理 (useState)

首先,我們利用了 React 的 useState 來管理應用的狀態。在 App.js 中,我們使用了四個狀態變數來處理不同的需求:

  • users: 用來存放從後端取得的用戶列表,這是一個陣列,初始值為空陣列。
  • newUserName: 儲存用戶在新增會員時輸入的名稱。
  • updateUserName: 儲存用戶在更新會員時輸入的新的名稱。
  • userIdToUpdate: 儲存用戶在更新會員時輸入的用戶 ID。

這些狀態變數可以透過 setUsers、setNewUserName、setUpdateUserName 和 setUserIdToUpdate 來動態更新。這些變數在 React 組件的生命週期內,會根據使用者的操作而改變,並導致畫面重新渲染。

2. 使用 useEffect 來處理資料的初始化載入

useEffect 是 React 中用來處理副作用(side effects)的 Hook,在這個應用中,我們使用 useEffect 來在組件載入時從後端 API 獲取資料。具體來說,我們在 useEffect 中調用了 fetchUsers 函數,這個函數會在組件第一次被載入時自動執行,向後端發送 GET 請求來獲取所有的會員資料。

以下是 useEffect 的簡單工作流程:

useEffect(() => {
  fetchUsers(); // 組件載入後獲取用戶資料
}, []); // 空陣列表示該副作用只在組件首次載入時執行一次

3. 與後端 API 互動

React 前端與後端 API 的互動是透過 axios 這個 HTTP 客戶端庫來實現的。axios 允許我們使用簡單的函數來發送 GET、POST、PUT、DELETE 等 HTTP 請求,並處理回應。在這個應用中,分別有三個主要的操作:

  • 獲取用戶列表 (GET 請求):
    我們使用 axios.get 來向 /users 路徑發送 GET 請求,並將後端回應的用戶資料存入 users 狀態變數中,這樣就能夠在頁面上顯示用戶列表。

    const fetchUsers = async () => {
      try {
        const response = await axios.get('/users'); // 向後端發送 GET 請求
        setUsers(response.data); // 將獲取到的用戶列表設定到狀態中
      } catch (error) {
        console.error('Error fetching users:', error); // 處理錯誤
      }
    };
    
  • 新增用戶 (POST 請求):
    當使用者在表單中輸入會員名稱並點擊 "新增會員" 按鈕後,createUser 函數會發送 POST 請求到 /users,新增一個新的用戶。

    const createUser = async () => {
      try {
        if (!newUserName) return; // 如果沒有輸入用戶名稱,則不執行
        await axios.post('/users', { name: newUserName }); // 發送 POST 請求新增用戶
        setNewUserName(''); // 清空輸入框
        fetchUsers(); // 重新獲取用戶列表
      } catch (error) {
        console.error('Error creating user:', error); // 處理錯誤
      }
    };
    
  • 更新用戶 (PUT 請求):
    當使用者輸入用戶 ID 和新的名稱後,點擊 "更新會員" 按鈕,updateUser 函數會發送 PUT 請求來更新對應的用戶資料。

    const updateUser = async () => {
      try {
        if (!userIdToUpdate || !updateUserName) return; // 檢查是否輸入了用戶 ID 和新名稱
        await axios.put(`/users/${userIdToUpdate}`, { name: updateUserName }); // 發送 PUT 請求更新用戶資料
        setUserIdToUpdate(''); // 清空輸入框
        setUpdateUserName(''); // 清空輸入框
        fetchUsers(); // 重新獲取用戶列表
      } catch (error) {
        console.error('Error updating user:', error); // 處理錯誤
      }
    };
    
  • 刪除用戶 (DELETE 請求):
    當使用者點擊 "刪除" 按鈕後,deleteUser 函數會發送 DELETE 請求,從後端刪除對應的用戶資料。

    const deleteUser = async (id) => {
      try {
        await axios.delete(`/users/${id}`); // 發送 DELETE 請求刪除用戶
        fetchUsers(); // 重新獲取用戶列表
      } catch (error) {
        console.error('Error deleting user:', error); // 處理錯誤
      }
    };
    

4. 表單處理與畫面更新

我們的前端頁面有兩個主要的表單區域,一個用來新增會員,另一個用來更新會員。使用者輸入名稱或 ID 時,這些值會即時存入對應的狀態變數,並在點擊按鈕時觸發相應的函數。

  • 新增會員的表單包括一個輸入框和一個按鈕,輸入框綁定了 newUserName,按鈕則綁定 createUser 函數。
  • 更新會員的表單有兩個輸入框,一個是會員 ID,一個是新的會員名稱,分別綁定 userIdToUpdate 和 updateUserName,按鈕則綁定 updateUser 函數。

每次新增、更新或刪除會員後,React 組件會自動重新渲染,並顯示最新的用戶列表,這是因為我們在每次操作後都會呼叫 fetchUsers 函數來更新 users 狀態。

5. 用戶列表顯示

在表格區域中,我們使用了 map 函數來遍歷 users 陣列,並生成每個用戶的表格行。對於每個用戶,我們顯示其 ID、名稱,並附上 "刪除" 按鈕。當點擊刪除按鈕時,會呼叫 deleteUser 函數,根據用戶的 ID 將其從資料庫中刪除。

這個 App.js 檔案展示了 React 前端如何透過 API 與後端服務進行互動。React 的 useState 和 useEffect Hook 幫助我們簡化了狀態管理和資料載入的邏輯,而 axios 庫則讓我們能夠輕鬆發送 HTTP 請求。在此基礎上,這個用戶管理系統可以進一步擴展,加入更多功能如驗證、分頁、排序等。

總結

這篇文章結合了使用Rust + React作為全端開發的基礎,回顧一開始從 Python 開發者的角度切入,到目前為止已經經歷了27天,對於 Rust 雖然不敢說熟悉,但是也因此增加了很多基礎知識與概念,現在再加上 React 的前端開發進來,可以說是在網頁開發上的一大進展,雖然 Python 的 Django 與 flask 等框架也是提供了非常簡易的網頁開發能力,相較之下 Rust 的 API 撰寫上當然也是比較困難的,但為了使用到 Rust 的高效能,讓它能夠擔任後端處理的工作也是相當不錯的選擇。

React 作為目前最熱門的前端框架之一,自然也是有許多可以適用的套件能供開發者選擇,由於今天的文章當中僅以 App.js 單一檔案為範例,再接下來,我們需要補充最後一個Web應用開發必要的部分,來解釋如果我的網頁需要有多個分頁,則應該怎麼去修改,畢竟總不能只使用一個分頁就完成整個網站的開發吧

也趁這個機會,下一篇我們將會再次說明 React 網頁開發的方式是怎麼一回事,希望也能提供給對 React 不熟悉的人有更進一步的認識囉


上一篇
[Day 26] Rust 的 Web 應用(三):使用 Rocket 與 MongoDB 建立 RESTful API
下一篇
[Day 28] Rust 的 Web 應用(五):React 分頁管理
系列文
從 Python 開發者的角度學習 Rust —— 從語法基礎到實戰應用 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言